import * as fs from "fs";
import * as path from "path";
import { consts } from "../../pkg-00-const";
export const lookup = (dir: string, regExp: RegExp, set: Set<string> = new Set()) => {
  fs.readdirSync(dir).forEach(folder_or_file_name => {
    let pagePath = path.resolve(dir, folder_or_file_name);
    if (fs.statSync(pagePath).isDirectory()) {
      lookup(pagePath, regExp, set);
    } else if (regExp.test(folder_or_file_name)) {
      set.add(pagePath.replace(/\\/g, "/"));
    }
  });
  return set;
};
export const write = (content: string, fileName: string) => {
  const dir = searchFolderUp(consts.virtualFolderName);
  console.log(`writeDir: ${dir}`);
  fs.writeFileSync(path.resolve(dir, fileName), content, { encoding: "utf-8" });
};
export const pickup = (folderName: string, pathRegExp: RegExp, textPicks: string[]) => {
  const pickDirs = searchFolder(folderName);
  return pickDirs.map(pickDir => pickupOne(pathRegExp, textPicks, pickDir)).reduce((a, b) => a.concat(b), []);
};
const pickupOne = (pathRegExp: RegExp, textPicks: string[], pickDir: string, set: Set<string> = new Set()) => {
  const srcAbsolutePath = path.resolve(searchFolderUp("src"));
  const REGEXP = new RegExp(textPicks.map(s => (s === "?" ? "(\\w+)" : s)).join("\\s+"));
  return Array.from(lookup(pickDir, pathRegExp, set))
    .map(filePath => {
      const str = fs.readFileSync(filePath, { encoding: "utf-8" });
      const folderAbsolutePath = path.resolve(filePath, "..").replace(/\\/g, "/");
      const folderName = folderAbsolutePath.substring(folderAbsolutePath.lastIndexOf("/") + 1);
      const fixPath = folderAbsolutePath.substring(pickDir.length);
      const folderPath = `@${folderAbsolutePath.substring(srcAbsolutePath.length)}`;
      const picksall = REGEXP.exec(str);
      if (!picksall) {
        throw new Error(`${filePath} exec Error.`);
      } else {
        return { filePath, folderPath, folderName, fixPath, picks: picksall.slice(1) };
      }
    })
    .filter(Boolean);
};
export const searchFolderUp = (folderName: string) => {
  let dir = __dirname.replace(/\\/g, "/");
  let searchCount = maxSearchCount;
  while (!fs.readdirSync(dir).includes(folderName)) {
    dir = path.resolve(dir, "..");
    if (0 === --searchCount) throw new Error(`Cannot find folder: ${folderName} from ${dir}`);
  }
  dir = path.resolve(dir, folderName);
  return dir;
};
export const searchFolder = (folderName: string, frse = { from: searchFolderUp("src"), searchCount: 0 }) => {
  if (frse.searchCount >= maxSearchCount) {
    return [];
  } else {
    let results: string[] = [];
    const folderNames = fs
      .readdirSync(frse.from)
      .filter(folderName => fs.statSync(path.resolve(frse.from, folderName)).isDirectory())
      .sort((a, b) => a.localeCompare(b));
    if (folderNames.length === 0) {
      //
    } else if (folderNames.includes(folderName)) {
      results.push(path.resolve(frse.from, folderName));
    } else {
      folderNames //e
        .map(fn => path.resolve(frse.from, fn))
        .forEach(fn =>
          Array.prototype.push //e
            .apply(results, searchFolder(folderName, { from: fn, searchCount: frse.searchCount + 1 }))
        );
    }
    return results;
  }
};
export const writeTemplate = (templateFileName: string, pickups: ReturnType<typeof pickup>, targetFileName: string) => {
  const templateFilePath = path.resolve(searchFolderUp(consts.virtualFolderName), templateFileName);
  const templateContent = fs.readFileSync(templateFilePath, { encoding: "utf-8" });
  const filledContent = templateContent.replace(TEMPLATE_FILLING_REG(), $1 => {
    return `\n${pickups
      .map(pickupObj =>
        TEMPLATE_FILLING_REG()
          .exec($1)[1]
          .replace(FILL_POSI(), $1 => readValue(pickupObj, FILL_POSI().exec($1)[1]))
      )
      .join("\n")}\n`;
  });
  write(`${filledContent}`, targetFileName);
};
export const copyFileByFolder = (sourceFolder: string, targetFolder: string, fileFixPath: string, cover: boolean = true) => {
  const rootDir = path.resolve(searchFolderUp("src"), "..");
  const [source, target] = [`${sourceFolder}${fileFixPath}`, `${targetFolder}${fileFixPath}`];
  const mkdirs = fileFixPath.split(/\/|\\/g).filter(Boolean);
  if (cover === false && fs.existsSync(target)) {
    console.log(`Skip: \${caseDir}${source.substring(rootDir.length)} => \${caseDir}${target.substring(rootDir.length)}`);
  } else {
    console.log(`Copy: \${caseDir}${source.substring(rootDir.length)} => \${caseDir}${target.substring(rootDir.length)}`);
    for (let folder = `${targetFolder}`; mkdirs.length > 0; folder += `/${mkdirs.shift()}`) {
      if (!fs.existsSync(folder)) fs.mkdirSync(folder);
    }
    fs.copyFileSync(source, target);
  }
};
export const copyFilesByFolder = (sourceFolder: string, targetFolder: string, cover: boolean = true) => {
  const fileFixPaths = Array.from(lookup(sourceFolder, /.*/)).map(fileFullPath => fileFullPath.substring(sourceFolder.length));
  fileFixPaths.forEach(fileFixPath => copyFileByFolder(sourceFolder, targetFolder, fileFixPath, cover));
};
export const deleteFolder = (path: string) => {
  if (fs.statSync(path).isFile()) fs.unlinkSync(path);
  else if (fs.statSync(path).isDirectory()) {
    fs.readdirSync(path).forEach(file => deleteFolder(`${path}/${file}`));
    fs.rmdirSync(path);
  }
};
const readValue = (root: any, propertyPath: string) => {
  const properties = splitProperties(propertyPath);
  let obj = root;
  for (let i = 0; i < properties.length; i++) {
    const crrtKey = __parseProperty(properties[i]);
    obj = obj[crrtKey];
    if (obj === null || obj === undefined) {
      break;
    }
  }
  return obj;
};
const splitProperties = (path: string) => path.replace(PROP_SPLIT(), "-").split("-").filter(Boolean);
const __parseProperty = (property: string) => (/[1-9]*[0-9]/.test(property) ? Number.parseInt(property) : property);
const maxSearchCount = 7;
const PROP_SPLIT = () => new RegExp("(\\]\\.)|(\\]\\[)|(\\[)|(\\])|(\\.)", "g");
const FILL_POSI = () => new RegExp("\\$\\{(.+?)\\}", "g");
const TEMPLATE_FILLING_REG = () => new RegExp("\\n*/\\*-+filling\\n*((?:.|\\n)+?)\\n*filling-+\\*/\\n*", "g");
